<script setup lang="ts">
import type { UploadFile, UploadProps, UploadRequestOptions } from 'element-plus';

import { reactive, ref } from 'vue';

import { VIcon } from '@croco/framework/icons';
import { sizeFormat } from '@croco/framework/utils';

import { ElMessage } from 'element-plus';

import { useUploadManager } from './chunk-upload-chain';

interface ParamsProps {
    from: string;
    service: string;
    p1?: any;
    p2?: any;
    p3?: any;
    [key: string]: any;
}

const props = defineProps({
    action: { type: String, default: '/file/upload' },
    chunkSize: { type: Number, default: 5_242_880 },
    concurrency: { type: Number, default: 3 },
});

const uploader = useUploadManager(props.chunkSize, props.concurrency);

const uploaderRef = ref();
const isView = ref<boolean>(false);
const properties = reactive({
    visible: false,
    fileList: [],
    params: {} as ParamsProps,
    onClose: () => {
        properties.visible = false;
    },
    onSelected: (type: string) => {
        const item = document.querySelectorAll('.smart-uploader input[name="file"]')[0] as HTMLElement;
        type === 'file' ? item.removeAttribute('webkitdirectory') : item.setAttribute('webkitdirectory', '');
        item.click();
    },
    onOperation: (type: string, file?: any) => {
        switch (type) {
            case 'clear': {
                uploader.abort();
                uploaderRef.value?.clearFiles();
                break;
            }
            case 'remove': {
                uploader.abort(file.raw.uid);
                uploaderRef.value?.handleRemove(file);
                break;
            }
            case 'upload': {
                uploaderRef.value?.submit();
                break;
            }
            case 'retry': {
                console.warn(`错误文件${file.name}重新上传`);
                uploaderRef.value?.handleRemove(file);
                uploaderRef.value?.handleStart(file.raw);
            }
        }
    },
    onPercentage: (file: any) => {
        switch (file.status) {
            case 'fail': {
                return `失败(${file.response.message})`;
            }
            case 'ready': {
                return '等待';
            }
            case 'success': {
                return '完成';
            }
            case 'uploading': {
                switch (file.raw.status) {
                    case 'computing': {
                        return `正在加载(${file.percentage}%)`;
                    }
                    case 'merging': {
                        return '正在合并';
                    }
                    case 'uploading': {
                        return `正在上传(${file.percentage}%)`;
                    }
                    default: {
                        return '未知';
                    }
                }
            }
        }
    },
});

const onChange = (file: any) => {
    // 当文件出错后重新上传时会再次该函数
    if (file.status === 'ready' && !Object.prototype.hasOwnProperty.call(file.raw, 'params')) {
        file.raw.params = { ...properties.params };
    }
};

const onBeforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
    if (rawFile.size / 1024 / 1024 / 1024 > 2) {
        ElMessage({ message: '单文件最大2GB', grouping: true, type: 'warning' });
        return false;
    }
    return true;
};

const onHttpRequest = (content: UploadRequestOptions): any => {
    uploader.add(props.action, content);
};

const onProgress = (event: any, file: any) => {
    // 细化上传过程（计算MD5,断点校验,上传分块,合并文件）
    file.raw.status = event.status;
};

const onSuccess = (response: any, file: UploadFile) => {
    if (response.code === 200) {
        uploaderRef.value?.handleRemove(file);
    } else {
        file.status = 'fail';
    }
};

// 展示弹窗
const show = (value: ParamsProps | string) => {
    if (typeof value === 'object') {
        properties.params = value;
    } else {
        isView.value = value === 'header';
    }
    properties.visible = true;
};

defineExpose({ show });
</script>

<template>
    <el-dialog
        v-model="properties.visible"
        class="smart-uploader"
        title="文件上传"
        width="800px"
        :before-close="properties.onClose"
        :close-on-click-modal="false"
        :draggable="true"
        :align-center="true"
    >
        <!-- 上传主体区域 -->
        <div class="upload-container">
            <el-upload
                ref="uploaderRef"
                v-model:file-list="properties.fileList"
                :auto-upload="false"
                class="drag-area"
                :class="[{ disabled: isView }]"
                :disabled="isView"
                :drag="true"
                :multiple="true"
                :on-change="onChange"
                :http-request="onHttpRequest"
                :on-progress="onProgress"
                :on-success="onSuccess"
                :before-upload="onBeforeUpload"
                :show-file-list="false"
            >
                <template #trigger>
                    <div class="upload-trigger">
                        <el-icon class="upload-icon">
                            <VIcon icon="line-md:cloud-alt-upload-filled-loop" />
                        </el-icon>
                        <div class="upload-text">
                            <span class="tip-title">
                                点击选择
                                <el-link link type="primary" @click.stop="properties.onSelected('file')">上传文件</el-link>
                                <el-divider direction="vertical" />
                                <el-link link type="primary" @click.stop="properties.onSelected('folder')">上传目录</el-link>
                                或 拖拽文件到这里
                            </span>
                            <p class="tip-sub">支持批量上传，单文件最大 2GB</p>
                        </div>
                    </div>
                </template>
            </el-upload>

            <!-- 文件列表 -->
            <el-table :data="properties.fileList" class="file-table" height="45vh">
                <el-table-column label="文件列表" class-name="file-list">
                    <template #header>
                        <div class="action-bar">
                            <div class="action-bar__left"></div>
                            <div class="action-bar__right">
                                <el-button type="primary" @click="properties.onOperation('upload')">
                                    <template #icon>
                                        <VIcon icon="icon:upload" />
                                    </template>
                                    上传
                                </el-button>
                                <el-button type="danger" @click="properties.onOperation('clear')">
                                    <template #icon>
                                        <VIcon icon="icon:delete" />
                                    </template>
                                    清除
                                </el-button>
                            </div>
                        </div>
                    </template>

                    <el-table-column align="right" fixed="left" header-align="center" label="序 号" type="index" width="70">
                        <template #default="{ $index }">
                            {{ $index + 1 }}
                        </template>
                    </el-table-column>
                    <el-table-column prop="name" label="文件名" min-width="220" show-overflow-tooltip>
                        <template #default="scope">
                            <div class="file-name">
                                <span class="name-text">{{ scope.row.name }}</span>
                            </div>
                        </template>
                    </el-table-column>
                    <el-table-column prop="size" label="大小" width="110">
                        <template #default="scope">
                            {{ sizeFormat(scope.row.size) }}
                        </template>
                    </el-table-column>
                    <el-table-column label="进度" width="200">
                        <template #default="scope">
                            <el-text :type="scope.row.status === 'fail' ? 'danger' : 'primary'">{{ properties.onPercentage(scope.row) }}</el-text>
                        </template>
                    </el-table-column>
                    <el-table-column label="操作" width="90" align="center">
                        <template #default="{ row }">
                            <el-button v-if="row.status === 'fail'" type="primary" link @click="properties.onOperation('retry', row)">
                                <template #icon>
                                    <VIcon icon="icon:refresh-right" />
                                </template>
                            </el-button>
                            <el-button type="danger" link @click="properties.onOperation('remove', row)">
                                <template #icon>
                                    <VIcon icon="icon:delete" />
                                </template>
                            </el-button>
                        </template>
                    </el-table-column>
                </el-table-column>
                <template #empty>
                    <el-empty description="暂无上传文件" />
                </template>
            </el-table>
        </div>
    </el-dialog>
</template>

<style lang="scss" scoped>
.smart-uploader {
    :deep(.el-dialog__header) {
        margin-right: 0;
        background: linear-gradient(120deg, #f8f9fc, #eef2f6);
        border-radius: 6px 6px 0 0;
    }

    .config-panel {
        margin: -10px 0 12px;
        border: none;

        :deep(.el-collapse-item__header) {
            padding-left: 12px;
            font-size: 13px;
            color: var(--el-color-info);
        }
    }

    .upload-container {
        position: relative;

        .drag-area {
            :deep(.el-upload-dragger) {
                border: 2px dashed var(--el-border-color);
                border-radius: 8px;
                transition: all 0.3s;
                padding: 20px;

                &:hover {
                    border-color: var(--el-color-primary);
                    background: rgba(var(--el-color-primary-rgb), 0.03);
                }
            }

            &.is-dragover {
                transform: scale(0.98);
            }

            .upload-trigger {
                text-align: center;

                .upload-icon {
                    font-size: 48px;
                    color: var(--el-color-primary);
                    margin-bottom: 2px;
                }

                .tip-title {
                    --tip-title-font-size: 16px;
                    font-size: var(--tip-title-font-size);
                    color: var(--el-text-color-primary);
                    margin: 0;
                    display: flex;
                    justify-content: center;

                    :deep(span) {
                        font-size: var(--tip-title-font-size);
                    }

                    .el-button {
                        padding: 4px 6px;
                    }
                }

                .tip-sub {
                    font-size: 12px;
                    color: var(--el-text-color-secondary);
                    margin: 6px 0 0;
                }
            }

            .is-disabled {
                .el-button {
                    cursor: not-allowed;
                }
            }
        }

        .file-table {
            margin-top: 16px;
            border-radius: 6px;

            :deep(.file-list) {
                background: transparent;
            }

            .file-name {
                display: flex;
                align-items: center;
                gap: 8px;

                .name-text {
                    flex: 1;
                }
            }
        }

        .action-bar {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin: 0;
        }
    }
}
</style>
